#include <dim-sum/errno.h>
#include <dim-sum/bug.h>
#include <linux/compiler.h>
#include <dim-sum/types.h>
#include <dim-sum/init.h>
#include <dim-sum/stringify.h>
#include <asm/exception.h>
#include <asm/cpu_esr.h>
#include <asm/elx_sync.h>
#include <asm/memfault.h>
#include <dim-sum/sched.h>

/**
 * 异常类处理函数表
 */
static handle_ec handle_ec_funtab[ESR_ELx_EC_MAX] = {
	[0 ... ESR_ELx_EC_MAX-1] = handle_ec_unknown,
	[ESR_ELx_EC_UNKNOWN] = handle_ec_unknown,
	[ESR_ELx_EC_FP_ASIMD] = handle_fpsimd_acc,
	[ESR_ELx_EC_SVC64] = handle_svc64,
	[ESR_ELx_EC_SYS64] = handle_sys64,
	[ESR_ELx_EC_SVE] = handle_sve_acc,
	[ESR_ELx_EC_IABT_LOW] = handle_inst_abort_low,
	[ESR_ELx_EC_IABT_CUR] = handle_inst_abort_cur,
	[ESR_ELx_EC_PC_ALIGN] = handle_pc_align,
	[ESR_ELx_EC_DABT_LOW] = handle_data_abort_low,
	[ESR_ELx_EC_DABT_CUR] = handle_data_abort_cur,
	[ESR_ELx_EC_SP_ALIGN] = handle_sp_align,
	[ESR_ELx_EC_FP_EXC64] = handle_fpsimd_exc64,
	[ESR_ELx_EC_BREAKPT_LOW] = handle_breakpt_low,
};


long handle_ec_unknown(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	pr_info("handle_ec_unknown task[%s,%lu] addr:%lx current user stack:%lx current user pc:%lx\n", current->name,current, fault_addr, s->sp, s->pc);
	BUG();
	return -EFAULT;
}

long handle_fpsimd_acc(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	pr_info("handle_fpsimd_acc addr:%lx current user stack:%lx current user pc:%lx\n", fault_addr, s->sp, s->pc);
	BUG();
	return -EFAULT;
}

long handle_svc64(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	/**
	 * 目前使用函数表直接调用系统API
	 */
	pr_info("[%s %s %d] [pc:%lx sp:%lx] [fault_addr:%lx esr:%x]\n", current->name, __FUNCTION__, __LINE__, s->pc, s->sp, fault_addr, esr);
	BUG();
	return -EFAULT;
}

long handle_sys64(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	pr_info("[%s %s %d] [pc:%lx sp:%lx] [fault_addr:%lx esr:%x]\n", current->name, __FUNCTION__, __LINE__, s->pc, s->sp, fault_addr, esr);
	BUG();
	return -EFAULT;
}

long handle_sve_acc(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	BUG();
	return -EFAULT;
}

long handle_inst_abort_low(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	// pr_info("handle_inst_abort_low :%lx\n", fault_addr);
	return mem_fault(s, fault_addr, esr);
}

long handle_inst_abort_cur(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	return mem_fault(s, fault_addr, esr);
}

long handle_pc_align(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	BUG();
	return -EFAULT;
}

long handle_data_abort_low(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	return mem_fault(s, fault_addr, esr);
}

long handle_data_abort_cur(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	return mem_fault(s, fault_addr, esr);
}

long handle_sp_align(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	BUG();
	return -EFAULT;
}

long handle_fpsimd_exc64(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	BUG();
	return -EFAULT;
}

long handle_breakpt_low(struct exception_spot *s, u64 fault_addr, u32 esr)
{
	BUG();
	return -EFAULT;
}

long handle_elx_sync(struct exception_spot *s, unsigned long level)
{
	u64 addr, esr;
	long ret;
	u32 ecode;

	addr = mrs(far_el1);
	esr = mrs(esr_el1);
	ecode = ESR_ELx_EC(esr);
	/**
  	 * 如果level为1说明在内核态中
	 * 出现了sync异常目前只能hung住，这只是通常的设计
	 * 但以我之见，内核态也可以处理部分异常的
	 */
	if (level == 1) {
		ret = handle_ec_funtab[ecode](s, addr, (u32)esr);
	} else if (level == 0) {
		ret = handle_ec_funtab[ecode](s, addr, (u32)esr);
	} else {
		BUG();
	}
	return ret;
}
